package top.kaoshanji.p1ch03b;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import top.kaoshanji.u1constant.FileConstant;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

/**
 * 代码清单 p1ch03b-04：基于选择器的非阻塞式服务器端套接字的使用示例(保存网页内容第4种写法)
 * author kaoshanji
 * ^_^
 * create time 2025/1/10 10:21
 */
public class ServerSocketChannelExample02 {

    private static final Logger logger = LogManager.getLogger(ServerSocketChannelExample02.class);

    public static void main(String[] args) throws IOException {
        logger.info("...代码清单 p1ch03b-04：基于选择器的非阻塞式服务器端套接字的使用示例(保存网页内容第4种写法)...");

        ServerSocketChannelExample02 example = new ServerSocketChannelExample02();

        Set<URL> urls = new HashSet<>();
        urls.add(new URL("http://www.baidu.com/"));
        urls.add(new URL("http://www.taobao.com/market/sports/index.php"));
        urls.add(new URL("http://news.163.com/"));

        example.load(urls);
    }

    // ---------------公共方法，对外公开，主要流程都在这里----------------------

    public void load(Set<URL> urls) throws IOException {
        Map<SocketAddress, String> mapping = urlToSocketAddress(urls);
        Selector selector = Selector.open();
        for (SocketAddress address: mapping.keySet()) {
            register(selector, address);
        }
        int finished = 0, total = mapping.size();
        ByteBuffer buffer = ByteBuffer.allocate(32 * 1024);
        int len = -1;
        while (finished < total) {
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isValid() && key.isReadable()) {
                    SocketChannel channel = (SocketChannel)key.channel();
                    InetSocketAddress address = (InetSocketAddress)channel.getRemoteAddress();

                    String fileName = FileConstant.LOCAL_ROOT_PATH.concat("p1ch03b04-").concat(address.getHostName()).concat(".txt");

                    FileChannel descChannel = FileChannel.open(Paths.get(fileName), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
                    buffer.clear();

                    while ((len = channel.read(buffer)) > 0 || buffer.position() != 0) {
                        buffer.flip();
                        descChannel.write(buffer);
                        buffer.compact();
                    }
                    if (len == -1) {
                        finished++;
                        key.cancel();
                    }

                } else if (key.isValid() && key.isConnectable()) {
                    SocketChannel channel = (SocketChannel)key.channel();
                    boolean success = channel.finishConnect();
                    if (!success) {
                        key.cancel();
                    } else {
                        InetSocketAddress address = (InetSocketAddress)channel.getRemoteAddress();
                        String path = mapping.get(address);
                        String request = "GET " + path + " HTTP/1.0\r\n\r\nHost: " + address.getHostString() + "\r\n\r\n";
                        ByteBuffer header = ByteBuffer.wrap(request.getBytes(StandardCharsets.UTF_8));
                        channel.write(header);
                    }
                }
            }
        }

    }


    // ---------------私有方法，一般都是被过滤掉的，因为都是细节----------------------

    private void register(Selector selector, SocketAddress address) throws IOException {
        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(address);
        channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
    }

    private Map<SocketAddress, String> urlToSocketAddress(Set<URL> urls) {
        Map<SocketAddress, String> mapping = new HashMap<>();
        for (URL url: urls) {
            int port = url.getPort() != -1 ? url.getPort() : url.getDefaultPort();
            SocketAddress address = new InetSocketAddress(url.getHost(), port);
            String path = url.getPath();
            if (url.getQuery() != null) {
                path = path + "?" + url.getQuery();
            }
            mapping.put(address, path);
        }

        return mapping;
    }



}
